package com.msh.starter.id.generate.instance;


import com.msh.frame.interfaces.IIdGenerateServiceId;
import com.msh.frame.interfaces.IdGenerateable;
import lombok.extern.slf4j.Slf4j;

/**
 * 注 :
 *
 * 根据系统自己设置 serviceIdBitCount，indexBitCount serviceIdBitCount indexBitCount 加起来24位可以使用20年，每多一位年数除2
 * serviceIdBitCount为服务id所占位数 默认4位(0-15) indexBitCount为每秒内生成最大数 进过测试(100个线程生成1000万个id，时间所需463毫秒)
 * 进过测试(100个线程生成5000万个id，时间所需2196毫秒) indexBitCount=15时速度最快(已修改为到最大限制时增加毫秒数，速度与indexBitCount无关了，line98：lastCurrentTimeMillis++)
 * --------------------------------------------------------------- 可生成多个对象，每个对象生成的id唯一
 * 但多个对象生成的id可能重复, 生成id最后的indexBitCount 位数据中有自增效果， 可在分库分表时，取模可以平均分配数据 给需要分库分表的id 使用独立的id生成器对象
 * 保证id取模后的连续性
 */
@Slf4j
public class IdGenerate implements IdGenerateable {

  /**
   * 2018-01-01 00:00:00
   */
  private static final long ID_BEGIN_TIME = 1514736000000L;
  /**
   * 服务器id位移后的值
   */
  private static volatile long serviceIdBitShiftValue;
  /**
   * 最后一次index为0时的时间戳
   */
  private volatile long lastCurrentTimeMillis = 0L;
  /**
   * 每次生成一个id自增1，当大于indexBit时置0， 保证indexBitCount几个比特位一直处于自增状态 可在分库，分表时独立生成该对象来获取id
   */
  private volatile long lastIndexBit = 0;
  /**
   * 时间戳需要的位移位数
   */
  private final int currentTimeMillisDiffBitCount;
  /**
   * 计数累计
   */
  private volatile int i = 0;
  /**
   * 计数累计 最大值
   */
  private final int indexBit;


  /**
   * 生成大概16位数
   */
  public IdGenerate() {
    this(new OneServiceIdGenerateServiceId(), 12);
  }

  /**
   * @param indexBitCount 每毫秒生成id数量占用bit 不能大于32
   */
  public IdGenerate(IIdGenerateServiceId idGenerateServiceId, int indexBitCount) {
    if(indexBitCount < 0 || indexBitCount > 32){
      log.error("每毫秒生成id数量占用bit超出范围，请重新配置. indexBitCount: {}", indexBitCount);
      System.exit(1);
    }
    /**
     * 服务器id所占位数
     */
    int serviceIdBitCount = idGenerateServiceId.bit();
    this.currentTimeMillisDiffBitCount = serviceIdBitCount + indexBitCount;
    this.indexBit = (1 << indexBitCount) - 1;
    serviceIdBitShiftValue = ((long) idGenerateServiceId.serverId()) << indexBitCount;
  }

  @Override
  public synchronized Long getUniqueId() {
    long now = System.currentTimeMillis();
    if (now > lastCurrentTimeMillis) {
      lastCurrentTimeMillis = now;
      i = 0;
    }
    if (i++ > indexBit) {
      i = 0;
      lastCurrentTimeMillis++;
    }
    long destId = lastCurrentTimeMillis - ID_BEGIN_TIME;
    destId = (destId << currentTimeMillisDiffBitCount) + lastIndexBit + serviceIdBitShiftValue;
    lastIndexBit++;
    if (lastIndexBit > indexBit) {
      lastIndexBit = 0;
    }
    return destId;
  }


  public static void main(String[] args) {
    for(int i=1; i<20; i++){
      System.out.println("i: " +i +".  " + new IdGenerate(new IIdGenerateServiceId() {
        @Override
        public int serverId() {
          return 1;
        }

        @Override
        public int digit() {
          return 10;
        }

        @Override
        public int bit() {
          return 3;
        }
      }, i).getUniqueId());
    }
  }
}
